﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq.Mapping;
using Icst.Leaflike.Logging;

namespace Icst.Leaflike.Data
{
    public class Category : DataContext<Category>
    {
        protected static readonly string CACHE_ALL = "/Category/All";
        protected static readonly string CACHE_PIVOTS = "/Category/Pivots";
        protected static readonly string CACHE_SEARCH = "/Category/Search/";
        protected static HashSet<string> CACHE_ALL_SEARCH = new HashSet<string>();

        public tree_search_bycategoryResult[] Search(string phylum, string classid, string family, string genus, string keyword, int page, int sort, int pagesize, out int totalCount)
        {
            string[] kws = keyword.Split(' ');
            tree_search_bycategoryResult[] results = Search(phylum, classid, family, genus, keyword, kws);
            totalCount = results.Count();
            results = results.OrderBy(p => p.tree_name).Skip((page - 1) * pagesize).Take(pagesize).ToArray();
            return results;
        }
        private tree_search_bycategoryResult[] Search(string phylum, string classid, string family, string genus, string keyword, string[] kws)
        {
            try
            {
                tree_search_bycategoryResult[] results =
                    AllSearchResults(phylum, classid, family, genus)
                    .Where(p => kws.Any(k => k.Contains(p.tree_name) || p.tree_name.Contains(k) || p.class_name.Contains(k) || p.conv_name.Contains(k)) || string.IsNullOrEmpty(keyword)).ToArray();
                return results;
            }
            catch(Exception e)
            {
                Logger.Error(
                    string.Format("FAILED TO SEARCH phylum={0}, classid={1}, family={2}, genus={3}, keyword={4}.",
                    phylum, classid, family, genus, keyword) + Environment.NewLine + e.Message);
                return new tree_search_bycategoryResult[] { };
            }
        }
        private tree_search_bycategoryResult[] AllSearchResults(string phylum, string classid, string family, string genus)
        {
            string cachekey = CACHE_SEARCH + DataUtil.Nvl(phylum, string.Empty) + DataUtil.Nvl(classid, string.Empty) + DataUtil.Nvl(family, string.Empty) + DataUtil.Nvl(genus, string.Empty);
            if (!CACHE_ALL_SEARCH.Contains(cachekey))
                CACHE_ALL_SEARCH.Add(cachekey);

            return CachePolicy.GetIf<tree_search_bycategoryResult[]>(
                cachekey,
                () =>
                {
                    return Context.tree_search_bycategory(phylum, classid, family, genus)
                        .ToArray();
                });
        }

        public string[] Getphylums()
        {
            return CategoriesData
                .Select(p => p.phylum_id)
                .Distinct()
                .ToArray();
        }
        public string[] Getclasses(string phylum)
        {
            return CategoriesData
                .Where(p => p.phylum_id == phylum)
                .Select(p => p.class_id)
                .Distinct()
                .ToArray()
                .Where(p=>!string.IsNullOrEmpty(p))
                .ToArray();
        }
        public string[] Getfamilies(string classid)
        {
            return CategoriesData
                .Where(p => p.class_id == classid)
                .Select(p => p.family_id)
                .Distinct()
                .ToArray()
                .Where(p => !string.IsNullOrEmpty(p))
                .ToArray();
        }
        public string[] Getgenuses(string family)
        {
            return CategoriesData
                .Where(p => p.family_id == family)
                .Select(p => p.genus_id)
                .Distinct()
                .ToArray()
                .Where(p => !string.IsNullOrEmpty(p))
                .ToArray();
        }
        private CategoryData[] CategoriesData
        {
            get
            {
                try
                {
                    return CachePolicy.GetIf<CategoryData[]>(
                        CACHE_ALL,
                        () =>
                        {
                            return Context.tree_categories
                                .Select(p =>
                                    new CategoryData
                                    {
                                        category_id = p.category_id,
                                        class_id = p.class_id,
                                        family_id = p.family_id,
                                        genus_id = p.genus_id,
                                        phylum_id = p.phylum_id,
                                        sibling_tree_name = p.sibling_tree_name,
                                        symbiosis = p.symbiosis,
                                        tree_no = p.tree_no
                                    }).ToArray();
                        });
                }
                catch (Exception e)
                {
                    Logger.Error("FAILED TO get CategoriesData." + e.Message);
                    return new CategoryData[] { };
                }
            }
        }

        public tree_pivot[] FindNames(bool sort)
        {
            tree_pivot[] pivots = PivotsData;
            if (!sort)
                return pivots.ToArray();
            else
                return pivots.OrderBy(p => p.tree_name).ToArray();
        }
        public string[] FindNames(string prefix, int count)
        {
            tree_pivot[] pivots = PivotsData;

            List<string> names = new List<string>();
            foreach (var tree in pivots)
            {
                string[] convNames = tree.conv_name.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                string[] classNames = tree.class_names.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string name in convNames) { if (!string.IsNullOrEmpty(name.Trim())) names.Add(name.Trim()); }
                foreach (string name in classNames) { if (!string.IsNullOrEmpty(name.Trim())) names.Add(name.Trim()); }
            }

            return names.Distinct().Where(p=>p.StartsWith(prefix)).Take(count).ToArray();
        }
        private tree_pivot[] PivotsData
        {
            get
            {
                try
                {
                    return CachePolicy.GetIf<tree_pivot[]>(
                        CACHE_PIVOTS,
                        () => { return Context.tree_pivots.ToArray(); });
                }
                catch (Exception e)
                {
                    Logger.Error("FAILED TO get PivotsData." + e.Message);
                    return new tree_pivot[] { };
                }
            }
        }
        public override void Refresh()
        {
            base.Refresh();

            Logger.Info("REFRESH CachePolicy.Clear");
            try { CachePolicy.Delete(CACHE_ALL); }
            catch { }
            try { CachePolicy.Delete(CACHE_PIVOTS); }
            catch { }
            foreach (string key in CACHE_ALL_SEARCH)
            {
                try { CachePolicy.Delete(key); }
                catch { }
            }
        }
    }
}
